/*
 * Author: ALBACEA, JOHN PATRICK VJ
 * 
 * */
package com.example.timedcustomcamera;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;

import org.opencv.android.Utils;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Scalar;
import org.opencv.imgproc.Imgproc;

import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.hardware.Camera;
import android.hardware.Camera.Size;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.telephony.SmsManager;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class TimedCustomCameraActivity extends Activity implements OnClickListener, SurfaceHolder.Callback, Camera.PictureCallback{
	
	  //name for images that will be saved
	  private static final String TAG = "TIMED_CUSTOM_CAMERA";
	  
	  //type of save file for the camera
	  public static final int MEDIA_TYPE_IMAGE = 1;
	  public static final int MEDIA_TYPE_VIDEO = 2;
	  
	  //constants for messaging of the application
	  public static final String TARGET_PHONE = "09266677250";
	  public static final String TARGET_MESSAGE = "Something Moved";

	  //variables for camera object and its holder
	  SurfaceView cameraView;
	  SurfaceHolder surfaceHolder;
	  Camera camera;

	  //UI variables
	  Button startStopButton;
	  TextView countdownTextView;
	  Handler timerUpdateHandler;
	  
	  //timer variables
	  boolean timelapseRunning = false;
	  int currentTime = 0;
	  final int SECONDS_BETWEEN_PHOTOS = 10;
	  int currentPic = 0;
	  
	  //change computation variables
	  float previousDifference = 0;
	  float percentageDifference = 0;
	  int changeExpiry = 0;
	  boolean hasSent = false;
	  int numOfTest = 0;
	  int numOfChange = 0;
	  
	  
	  
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        //used to mute the sound of the shutter of the camera
        AudioManager mgr = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
        mgr.setStreamMute(AudioManager.STREAM_SYSTEM, true);
        
        //clean out previous images for the next batch of images
        deletePreviousImages();
        
        //instantiate the objects of camera and its holder
        cameraView = (SurfaceView) this.findViewById(R.id.CameraView);
        surfaceHolder = cameraView.getHolder();
        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        surfaceHolder.addCallback(this);

        //instantiate buttons and textviews for the application
        countdownTextView = (TextView) findViewById(R.id.CountDownTextView);
        startStopButton = (Button) findViewById(R.id.CountDownButton);
        startStopButton.setOnClickListener(this);
        timerUpdateHandler = new Handler();
    }
    
    //to do before the application exits
    @Override
    public void onDestroy(){
    	//return volume
        AudioManager mgr = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
        mgr.setStreamMute(AudioManager.STREAM_SYSTEM, false);
        
        //stop the thread
        if(timelapseRunning){
	        timelapseRunning = false;
	        timerUpdateHandler.removeCallbacks(timerUpdateTask);
        }
        
        super.onDestroy();
    }

    //this will be done when the button is pressed
	@Override
	public void onClick(View arg0) {
		// TODO Auto-generated method stub
	    if (!timelapseRunning) {
	    	//start timer
	        startStopButton.setText("Stop");
	        timelapseRunning = true;
	        timerUpdateHandler.post(timerUpdateTask);
	      } else {
	    	//stop timer
	        startStopButton.setText("Start");
	        timelapseRunning = false;
	        timerUpdateHandler.removeCallbacks(timerUpdateTask);
	      }
	}

	  //timer object
	  private Runnable timerUpdateTask = new Runnable() {
		    public void run() {
		      
		      if (currentTime < SECONDS_BETWEEN_PHOTOS) {
		    	//when its not yet time, do not take picture
		        currentTime++;
		      } else {
		    	//when it is time, take picture
	    	    camera.takePicture(null, null, null, TimedCustomCameraActivity.this);
		        currentPic++;
		        currentTime = 0;

		        
		      }

		      //update screen
		      timerUpdateHandler.postDelayed(timerUpdateTask, 1000);
		      countdownTextView.setText("" + currentTime);
		    }
		  };
	
	//gets the optimal size of image so that the android can do image processing on it
    private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
        final double ASPECT_TOLERANCE = 0.05;
        double targetRatio = (double) w / h;
        if (sizes == null) return null;

        Size optimalSize = null;
        double minDiff = Double.MAX_VALUE;

        int targetHeight = h;

        // Try to find an size match aspect ratio and size
        for (Size size : sizes) {
            double ratio = (double) size.width / size.height;
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }

        // Cannot find the one match the aspect ratio, ignore the requirement
        if (optimalSize == null) {
            minDiff = Double.MAX_VALUE;
            for (Size size : sizes) {
                if (Math.abs(size.height - targetHeight) < minDiff) {
                    optimalSize = size;
                    minDiff = Math.abs(size.height - targetHeight);
                }
            }
        }
        return optimalSize;
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        // Now that the size is known, set up the camera parameters and begin
        // the preview.
    	
        Camera.Parameters parameters = camera.getParameters();
        Log.d("TEST", "PASSING LINE");
        if(parameters.equals(null)){
        	Log.d("PARAMS", "NULL POINTER");
        }

        List<Size> sizes = parameters.getSupportedPreviewSizes();
        Size optimalSize = getOptimalPreviewSize(sizes, w, h);
        parameters.setPreviewSize(optimalSize.width, optimalSize.height);

        camera.setParameters(parameters);
        camera.startPreview();
    }

	@Override
	public void surfaceCreated(SurfaceHolder arg0) {
		// TODO Auto-generated method stub
		//get camera instance
		camera = getCameraInstance();
		Log.d("HELLO", "MERON NA CAMERA");
	    
	    try {
	      camera.setPreviewDisplay(arg0);
	      Camera.Parameters parameters = camera.getParameters();
	      if (this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) {
	        parameters.set("orientation", "portrait");
	        camera.setDisplayOrientation(90);
	      }
	      camera.setParameters(parameters);
	    } catch (IOException exception) {
	      camera.release();
	    }
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder arg0) {
		// TODO Auto-generated method stub
	    camera.stopPreview();
	    camera.release();
	}

	//runs when picture is taken by android
	@Override
	public void onPictureTaken(byte[] arg0, Camera arg1) {
		// TODO Auto-generated method stub
	        File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE, currentPic);
	        if (pictureFile == null){
	            Log.d(TAG, "Error creating media file, check storage permissions: ");
	            return;
	        }	    
	        try {
	          OutputStream imageFileOS = new FileOutputStream(pictureFile);
	          imageFileOS.write(arg0);
	          imageFileOS.flush();
	          imageFileOS.close();

	          Toast t = Toast.makeText(this, "Saved JPEG!", Toast.LENGTH_SHORT);
	          t.show();
	          
	          Log.d("FILE_PATH", pictureFile.getName());
	          //create edgeDetected counterpart
	          Bitmap binaryImage = implementEdgeDetection("/Pictures/MyCameraApp/"+pictureFile.getName());
	          File binaryFile = getBinaryMediaFile(MEDIA_TYPE_IMAGE,pictureFile.getName());
	          
	          try {
	              FileOutputStream out = new FileOutputStream(binaryFile);
	              binaryImage.compress(Bitmap.CompressFormat.JPEG, 100, out);
		       } catch (Exception e) {
		              e.printStackTrace();
		       }
	        } catch (Exception e) {
	          Toast t = Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT);
	          t.show();
	        }
	        camera.startPreview();
	        
	        if(numOfTest>=5){
	        	computeDifference(/*"/Pictures/MyCameraAppBinary/"+*/pictureFile.getName());
	        } else{
	        	numOfTest++;
	        	Log.d("DATA_GATHERING", "Sample #"+numOfTest);
	        }
	}
	
    public static Camera getCameraInstance(){
        Camera c = null;
        try {
            c = Camera.open(); // attempt to get a Camera instance
        }
        catch (Exception e){
            // Camera is not available (in use or does not exist)
        }
        return c; // returns null if camera is unavailable
    }
    
	/** Create a File for saving an image or video */
	private static File getOutputMediaFile(int type, int cp){
	    // To be safe, you should check that the SDCard is mounted
	    // using Environment.getExternalStorageState() before doing this.

	    File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
	              Environment.DIRECTORY_PICTURES), "MyCameraApp");
	    // This location works best if you want the created images to be shared
	    // between applications and persist after your app has been uninstalled.

	    // Create the storage directory if it does not exist
	    if (! mediaStorageDir.exists()){
	        if (! mediaStorageDir.mkdirs()){
	            Log.d("MyCameraApp", "failed to create directory");
	            return null;
	        }
	    }

	    // Create a media file name
	    File mediaFile;
	    if (type == MEDIA_TYPE_IMAGE){
	        mediaFile = new File(mediaStorageDir.getPath() + File.separator +
	        "TIMED_IMG_"+ cp + ".jpg");	
	    } else {
	        return null;
	    }

	    return mediaFile;
	}

	/** Create a File for saving an image or video */
	private static File getBinaryMediaFile(int type, String filename){
	    // To be safe, you should check that the SDCard is mounted
	    // using Environment.getExternalStorageState() before doing this.

	    File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
	              Environment.DIRECTORY_PICTURES), "MyCameraAppBinary");
	    // This location works best if you want the created images to be shared
	    // between applications and persist after your app has been uninstalled.

	    // Create the storage directory if it does not exist
	    if (! mediaStorageDir.exists()){
	        if (! mediaStorageDir.mkdirs()){
	            Log.d("MyCameraAppBinary", "failed to create directory");
	            return null;
	        }
	    }

	    // Create a media file name
	    File mediaFile;
	    if (type == MEDIA_TYPE_IMAGE){
	        mediaFile = new File(mediaStorageDir.getPath() + File.separator + filename);	
	    } else {
	        return null;
	    }

	    return mediaFile;
	}
	
	/** Create a File for saving an image or video */
	private static File getChangeMediaFile(int type, String filename){
	    // To be safe, you should check that the SDCard is mounted
	    // using Environment.getExternalStorageState() before doing this.

	    File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
	              Environment.DIRECTORY_PICTURES), "MyCameraAppChange");
	    // This location works best if you want the created images to be shared
	    // between applications and persist after your app has been uninstalled.

	    // Create the storage directory if it does not exist
	    if (! mediaStorageDir.exists()){
	        if (! mediaStorageDir.mkdirs()){
	            Log.d("MyCameraAppChange", "failed to create directory");
	            return null;
	        }
	    }

	    // Create a media file name
	    File mediaFile;
	    if (type == MEDIA_TYPE_IMAGE){
	        mediaFile = new File(mediaStorageDir.getPath() + File.separator + filename);	
	    } else {
	        return null;
	    }

	    return mediaFile;
	}
	
	//cleans out directories for the next batch of images
	private void deletePreviousImages(){
		
		File dir = new File(Environment.getExternalStorageDirectory()+"/Pictures/MyCameraApp");
		
		if (dir.isDirectory()) {
	        String[] children = dir.list();
	        for (int i = 0; i < children.length; i++) {
	            new File(dir, children[i]).delete();
	        }
	    }

		File dir2 = new File(Environment.getExternalStorageDirectory()+"/Pictures/MyCameraAppBinary");
		
		if (dir2.isDirectory()) {
	        String[] children = dir2.list();
	        for (int i = 0; i < children.length; i++) {
	            new File(dir2, children[i]).delete();
	        }
	    }

		File dir3 = new File(Environment.getExternalStorageDirectory()+"/Pictures/MyCameraAppChange");
		
		if (dir3.isDirectory()) {
	        String[] children = dir3.list();
	        for (int i = 0; i < children.length; i++) {
	            new File(dir3, children[i]).delete();
	        }
	    }
	}
	
	//decodes image and scales it to reduce memory consumption
	private Bitmap decodeFile(File f){
	    try {
	        //Decode image size
	        BitmapFactory.Options o = new BitmapFactory.Options();
	        o.inJustDecodeBounds = true;
	        BitmapFactory.decodeStream(new FileInputStream(f),null,o);

	        //The new size we want to scale to
	        final int REQUIRED_SIZE=70;

	        //Find the correct scale value. It should be the power of 2.
	        int scale=1;
	        while(o.outWidth/scale/2>=REQUIRED_SIZE && o.outHeight/scale/2>=REQUIRED_SIZE)
	            scale++;

	        //Decode with inSampleSize
	        BitmapFactory.Options o2 = new BitmapFactory.Options();
	        o2.inSampleSize=scale;
	        return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
	    } catch (FileNotFoundException e) {}
	    return null;
	}

	//implements edge detection on pictures so that comparison may be done it
	private Bitmap implementEdgeDetection(String path){
		
		Bitmap returnBitmap;
		
		
		File dir = Environment.getExternalStorageDirectory();
		File startImageFile = new File(dir, path);

		Bitmap startImage = decodeFile(startImageFile);
		
		returnBitmap = startImage.copy(Config.ARGB_8888, true);
		
		Mat mImg = new Mat();
		        mImg = Utils.bitmapToMat(returnBitmap);

		        //Converting to grayscale
		        Mat mGray = new Mat(mImg.rows(), mImg.cols(), CvType.CV_8UC1, new
		Scalar(0));
		        Imgproc.cvtColor(mImg , mGray, Imgproc.COLOR_BGRA2GRAY, 4);
		        //Applying Canny
		        Imgproc.Canny(mGray, mGray, 80, 90);

		        //Converting back to 4 channel image
		        Imgproc.cvtColor(mGray , mImg, Imgproc.COLOR_GRAY2RGBA, 4);
		        returnBitmap.recycle();
		        System.gc();
		        returnBitmap = Bitmap.createBitmap(mImg.cols(), mImg.rows(),
		Bitmap.Config.ARGB_8888);
		        Utils.matToBitmap(mImg, returnBitmap);
		        
		return returnBitmap;
	}
	
	//computes the difference between the images and determines whether a message need to be sent or not
	private void computeDifference(String filename){
		
		int pixelDifferenceCount = 0;

		
//		File dir = Environment.getExternalStorageDirectory();
//		File startImageFile = new File(dir, "/Pictures/MyCameraAppBinary/TIMED_IMG_1.jpg");
//		File endImageFile = new File(dir, filename);
//		
//		Bitmap startImage = decodeFile(startImageFile);
//		Bitmap endImage = decodeFile(endImageFile);
		
		Bitmap startImage1 = BitmapFactory.decodeFile("/sdcard/Pictures/MyCameraAppBinary/TIMED_IMG_"+(numOfChange+1)+".jpg");
		Bitmap startImage2 = BitmapFactory.decodeFile("/sdcard/Pictures/MyCameraAppBinary/TIMED_IMG_"+(numOfChange+2)+".jpg");
		Bitmap startImage3 = BitmapFactory.decodeFile("/sdcard/Pictures/MyCameraAppBinary/TIMED_IMG_"+(numOfChange+3)+".jpg");
		Bitmap startImage4 = BitmapFactory.decodeFile("/sdcard/Pictures/MyCameraAppBinary/TIMED_IMG_"+(numOfChange+4)+".jpg");
		Bitmap startImage5 = BitmapFactory.decodeFile("/sdcard/Pictures/MyCameraAppBinary/TIMED_IMG_"+(numOfChange+5)+".jpg");
		Bitmap endImage = BitmapFactory.decodeFile("/sdcard/Pictures/MyCameraAppBinary/"+filename);
		
		int width = startImage1.getWidth();
		int height = endImage.getHeight();
		int pixelCount = width * height;
		int[] pixelsStart1 = new int[pixelCount];
		int[] pixelsStart2 = new int[pixelCount];
		int[] pixelsStart3 = new int[pixelCount];
		int[] pixelsStart4 = new int[pixelCount];
		int[] pixelsStart5 = new int[pixelCount];
		int[] pixelsEnd = new int[pixelCount];
		int[] pixels3 = new int[pixelCount];
		
		Bitmap changeImage = Bitmap.createBitmap(width, height, Config.ARGB_8888);

		startImage1.getPixels(pixelsStart1, 0, width, 0, 0, width, height);
		startImage2.getPixels(pixelsStart2, 0, width, 0, 0, width, height);
		startImage3.getPixels(pixelsStart3, 0, width, 0, 0, width, height);
		startImage4.getPixels(pixelsStart4, 0, width, 0, 0, width, height);
		startImage5.getPixels(pixelsStart5, 0, width, 0, 0, width, height);
		endImage.getPixels(pixelsEnd, 0, width, 0, 0, width, height);

		for (int i = 0; i < pixelCount; i++) {
		    if (pixelsStart1[i] != pixelsEnd[i] && pixelsStart2[i] != pixelsEnd[i] && pixelsStart3[i] != pixelsEnd[i] && pixelsStart4[i] != pixelsEnd[i] && pixelsStart5[i] != pixelsEnd[i]) {
		    	pixelDifferenceCount++;
		    	pixels3[i] = Color.WHITE;
		    } else{
		    	pixels3[i] = Color.BLACK;
		    }
		}
		
//too close values in my opinion
		
//		boolean change1 = false;
//		boolean change2 = false;
//		boolean change3 = false;
//		boolean change4 = false;
//		boolean change5 = false;
//		
//		for (int i = 0; i < pixelCount; i++) {
//			change1 = (pixelsStart1[i] == Color.WHITE && pixelsEnd[i] == Color.BLACK) || (pixelsStart1[i] == Color.BLACK && pixelsEnd[i] == Color.WHITE);
//			change2 = (pixelsStart2[i] == Color.WHITE && pixelsEnd[i] == Color.BLACK) || (pixelsStart2[i] == Color.BLACK && pixelsEnd[i] == Color.WHITE);
//			change3 = (pixelsStart3[i] == Color.WHITE && pixelsEnd[i] == Color.BLACK) || (pixelsStart3[i] == Color.BLACK && pixelsEnd[i] == Color.WHITE);
//			change4 = (pixelsStart4[i] == Color.WHITE && pixelsEnd[i] == Color.BLACK) || (pixelsStart4[i] == Color.BLACK && pixelsEnd[i] == Color.WHITE);
//			change5 = (pixelsStart5[i] == Color.WHITE && pixelsEnd[i] == Color.BLACK) || (pixelsStart5[i] == Color.BLACK && pixelsEnd[i] == Color.WHITE);
//			
//		    if (change1 || change2 || change3 || change4 || change5) {
//		    	pixelDifferenceCount++;
//		    	pixels3[i] = Color.WHITE;
//		    } else{
//		    	pixels3[i] = Color.BLACK;
//		    }
//		}
		
		changeImage.setPixels(pixels3, 0, width, 0, 0, width, height);
        File changeFile = getChangeMediaFile(MEDIA_TYPE_IMAGE,filename);
        
        try {
            FileOutputStream out = new FileOutputStream(changeFile);
            changeImage.compress(Bitmap.CompressFormat.JPEG, 100, out);
	       } catch (Exception e) {
	              e.printStackTrace();
	       }
        
        if(hasSent && changeExpiry >= 2){
        	hasSent = false;
        	changeExpiry = 0;
        }
        
		
        previousDifference = percentageDifference;
		percentageDifference = (float) pixelDifferenceCount/pixelCount;
		
		if(Math.abs(percentageDifference-previousDifference) > 0.05 && !hasSent && numOfChange+6 != currentPic){
			Log.d("MESSAGE_STATUS", "message being sent");
			changeExpiry++;
			hasSent = true;
			sendSMS(TARGET_PHONE,TARGET_MESSAGE + " Change found in: " + filename);
		}else
		if(percentageDifference > 0.15){
			numOfTest = 0;
			numOfChange = currentPic;
		}
		Log.d("PIXEL_DIFFERENCE", ""+pixelDifferenceCount);
		Log.d("PIXEL_COUNT", ""+pixelCount);
		Log.d("PERCENTAGE_DIFFERENCE",""+percentageDifference);
		Log.d("CHANGE_DIFFERENCE", ""+Math.abs(percentageDifference-previousDifference));
		
		
		
	}
	
    //---sends an SMS message to another device---
    private void sendSMS(String phoneNumber, String message)
    {        
        PendingIntent pi = PendingIntent.getActivity(this, 0,
            new Intent(this, TimedCustomCameraActivity.class), 0);                
        SmsManager sms = SmsManager.getDefault();
        sms.sendTextMessage(phoneNumber, null, message, pi, null);
        pi.cancel();
    }    

}